package com.mkait.thief.service.impl;


import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradeFastpayRefundQueryRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeFastpayRefundQueryResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.mkait.thief.bean.vo.MkaitTokenBean;
import com.mkait.thief.domain.ChannelAccount;
import com.mkait.thief.domain.Complaint;
import com.mkait.thief.domain.Order;
import com.mkait.thief.domain.RefundVerify;
import com.mkait.thief.service.*;
import com.mkait.thief.util.HttpUtils;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.model.QueryOrderByOutTradeNoRequest;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.model.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.List;

@Service
@Slf4j
public class RefundServiceImpl implements RefundService {

    @Autowired
    private OrderService orderService;

    @Autowired
    private ChannelAccountService channelAccountService;

    @Autowired
    private ComplaintService complaintService;

    @Autowired
    private RefundVerifyService refundVerifyService;

    @Autowired
    private HttpUtils httpUtils;

    @Autowired
    private MkaitTokenBean mkaitTokenBean;

    /**
     * 退款
     * @param tradeNo
     */
    @Override
    public void runRefund(String tradeNo) {
        RefundVerify byId = refundVerifyService.getById(tradeNo);
        if (byId != null){
            log.info("退款任务已存在");
            return;
        }
        RefundVerify refundVerify = new RefundVerify();
        refundVerify.setStatus(0);
        refundVerify.setTradeNo(tradeNo);
        refundVerify.setRefundTime(LocalDateTime.now());
        refundVerifyService.saveOrUpdate(refundVerify);

        LambdaQueryWrapper<Order> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Order::getTradeNo,tradeNo);
        Order order = orderService.getOne(wrapper);
        Integer channelId = order.getChannelId();
        ChannelAccount ca = channelAccountService.getById(order.getChannelAccountId());
        switch (channelId){
            case 56:
            case 59:
                log.info(tradeNo + "进行支付宝退款");
                aliRefund(order,ca,0);
                break;
            case 71:
            case 72:
                log.info(tradeNo + "进行微信退款");
                wxRefund(order,ca,0);
                break;
        }
    }

    /**
     * 批量退款
     * @param complaintIds
     */
    @Override
    public void batchRefund(String[] complaintIds) {
        for (String complaintId : complaintIds) {
            LambdaQueryWrapper<Complaint> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(Complaint::getId,complaintId);
            Complaint complaint = complaintService.getOne(wrapper);
            String tradeNo = complaint.getTradeNo();
            runRefund(tradeNo);
        }
    }

    /**
     * 自动处理商家已同意退款
     */
    @Override
    public void refundService() {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime localDateTimeEnd = now.with(LocalTime.MAX).minusMinutes(10);
        LocalDateTime localDateTimeStart = now.with(LocalTime.MIN).plusMinutes(30);
        //如果当前时间在00：30以前则不执行
        if (now.isBefore(localDateTimeStart)){
            log.info("不超过当日00：30，不处理");
            return;
        }
        //如果当前时间在23：50以后则不执行
        if (now.isAfter(localDateTimeEnd)){
            log.info("已超过当日23：50，不再处理");
            return;
        }

        LambdaQueryWrapper<Complaint> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Complaint::getStatus,0).eq(Complaint::getMerchantStatus,1);
        List<Complaint> list = complaintService.list(wrapper);
        String successMsg = "判决成功商家余额已扣除,您需要人工联系买家进行退款！";
        String urlStr = "https://www.mkait.com/Manage/complaint/win.html";
        for (Complaint complaint : list) {
            String tradeNo = complaint.getTradeNo();
            Integer id = complaint.getId();
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("result",2);
            jsonObject.put("id",id);
            boolean isSuccess = httpUtils.sendPost(urlStr, jsonObject, successMsg);
            if (isSuccess){
                runRefund(tradeNo);
            }
        }
    }

    /**
     * 验证已退款订单退款状态
     */
    @Override
    public void verifyRefundService() {
        LambdaQueryWrapper<RefundVerify> rfWrapper = new LambdaQueryWrapper<>();
        rfWrapper.eq(RefundVerify::getStatus,0).lt(RefundVerify::getRefundTime,LocalDateTime.now().minusMinutes(2)).or()
                .eq(RefundVerify::getStatus,1).lt(RefundVerify::getRefundTime,LocalDateTime.now().minusMinutes(2));
        List<RefundVerify> list = refundVerifyService.list(rfWrapper);

        log.info("获取到{}条待验证退款信息，开始进行遍历验证",list.size());

        for (RefundVerify refundVerify : list) {
            String tradeNo = refundVerify.getTradeNo();
            String refundOrderId = refundVerify.getRefundOrderId();
            Integer status = 0;
            LambdaQueryWrapper<Order> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(Order::getTradeNo,tradeNo);
            Order order = orderService.getOne(wrapper);
            ChannelAccount ca = channelAccountService.getById(order.getChannelAccountId());

            Integer channelId = order.getChannelId();
            Map<String,String> infoMap = JSONObject.parseObject(ca.getParams(), HashMap.class);
            switch (channelId){
                case 56:
                case 59:

                    //获取支付渠道信息
                    String appId = infoMap.get("app_id");
                    String privateKey = infoMap.get("merchant_private_key");
                    String alipayPublicKey = infoMap.get("alipay_public_key");
                    try {
                        status = getAliRefundStatus(tradeNo,appId,privateKey,alipayPublicKey);
                    }catch (Exception e){
                        status = 1;
                    }
                    break;
                case 71:
                case 72:
                    //获取支付渠道信息
                    String merchantId = infoMap.get("mchid");
                    String apiKey = infoMap.get("apiKey");
                    String pem_key = infoMap.get("pem_key");
                    String merchantSerialNumber = ca.getName();
                    try {
                        status = getWxRefundStatus(tradeNo,merchantId,apiKey,merchantSerialNumber,pem_key,refundOrderId);
                    }catch (Exception e){
                        status = 1;
                    }

                    break;
            }
            log.info("退款订单号：" + tradeNo + "，退款状态：" + status);
            refundVerify.setStatus(status);
            refundVerify.setVerifyTime(LocalDateTime.now());
            refundVerifyService.saveOrUpdate(refundVerify);
        }
    }

    private Integer getWxRefundStatus(String tradeNo, String merchantId, String apiKey, String merchantSerialNumber, String pem_key, String refundOrderId) {
        //创建微信退款请求Authorization
        Config config = new RSAAutoCertificateConfig.Builder()
                .merchantId(merchantId)
                .privateKey(pem_key)
                .merchantSerialNumber(merchantSerialNumber)
                .apiV3Key(apiKey)
                .build();

        com.wechat.pay.java.service.refund.RefundService refundBuild = new com.wechat.pay.java.service.refund.RefundService.Builder().config(config).build();
        QueryByOutRefundNoRequest queryByOutRefundNoRequest = new QueryByOutRefundNoRequest();
        queryByOutRefundNoRequest.setOutRefundNo(refundOrderId);
        Refund refund = refundBuild.queryByOutRefundNo(queryByOutRefundNoRequest);
        Status status = refund.getStatus();
        if (status.equals(Status.SUCCESS)) {
            return 3;
        }else if (status.equals(Status.CLOSED) || status.equals(Status.ABNORMAL)){
            return 2;
        }else {
            return 1;
        }

    }


    @Override
    public void runRefund(Complaint complaint) {
        String successMsg = "判决成功商家余额已扣除,您需要人工联系买家进行退款！";
        String urlStr = "https://www.mkait.com/Manage/complaint/win.html";
        Integer id = complaint.getId();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("result",2);
        jsonObject.put("id",id);
        boolean isSuccess = httpUtils.sendPost(urlStr, jsonObject, successMsg);
        if (isSuccess){
            log.info(complaint.getTradeNo() + "魔卡退款成功，马上进行支付接口退款");
            runRefund(complaint.getTradeNo());
        }
    }

    @Override
    public void refuseRefund(Complaint complaint) {
        String successMsg = "判决成功";
        String urlStr = "https://www.mkait.com/Manage/complaint/win.html";
        Integer id = complaint.getId();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("result",1);
        jsonObject.put("id",id);
        boolean isSuccess = httpUtils.sendPost(urlStr, jsonObject, successMsg);
        if (!isSuccess){
            refuseRefund(complaint);
        }
    }

    /**
     * 阿里云（支付宝）退款逻辑
     * @param order
     * @param ca
     */
    private void aliRefund(Order order,ChannelAccount ca,int l){
        if (l >= 30){
            log.info("支付宝退款递归超过次数，强制结束");
            return;
        }

        try {
            String params = ca.getParams();
            Map<String,String> infoMap = JSONObject.parseObject(params, HashMap.class);

            //获取支付渠道信息
            String appId = infoMap.get("app_id");
            String privateKey = infoMap.get("merchant_private_key");
            String alipayPublicKey = infoMap.get("alipay_public_key");
            String tradeNo = order.getTradeNo();

            //获取投诉信息金额
            LambdaQueryWrapper<Complaint> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(Complaint::getTradeNo,tradeNo);
            Complaint complaint = complaintService.getOne(wrapper);
            BigDecimal price = null;
            //如果没有投诉，直接获取订单金额
            if (complaint != null){
                price = complaint.getPrice();
            }else {
                price = order.getTotalPrice();
            }
            //如果订单有短信提醒，扣取短信费用
            if (order.getSmsNotify() == null){
                price.subtract(order.getSmsPrice());
            }

            //阿里云退款
            AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do",appId,privateKey,"json","utf-8",alipayPublicKey,"RSA2");
            AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
            JSONObject bizContent = new JSONObject();
            bizContent.put("out_trade_no", tradeNo);
            bizContent.put("refund_amount", price.multiply(BigDecimal.valueOf(0.95)).setScale(2,BigDecimal.ROUND_DOWN));
            bizContent.put("out_request_no","Refund_" + tradeNo);
            bizContent.put("refund_reason","魔卡联盟投诉退款-收取部分手续费");

            request.setBizContent(bizContent.toString());
            AlipayTradeRefundResponse response = alipayClient.execute(request);
            if (!response.isSuccess()) {
                log.error("支付宝退款失败: {},进行递归确保退款成功", tradeNo);
                aliRefund(order,ca,++l);
                return;
            }else {
                log.info("支付宝退款成功: {}", tradeNo);
            }
        }catch (Exception e){
            throw new RuntimeException(e.getMessage(),e);
        }
    }

    private void wxRefund(Order order, ChannelAccount ca, int l){
        if (l >= 30){
            log.info("微信退款递归超过次数，强制结束");
            return;
        }

        try {
            String params = ca.getParams();
            Map<String,String> infoMap = JSONObject.parseObject(params, HashMap.class);

            //获取支付渠道信息
            String merchantId = infoMap.get("mchid");
            String apiKey = infoMap.get("apiKey");
            String pem_key = infoMap.get("pem_key");
            String tradeNo = order.getTradeNo();
            String merchantSerialNumber = ca.getName();

            //获取投诉信息金额
            LambdaQueryWrapper<Complaint> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(Complaint::getTradeNo,tradeNo);
            Complaint complaint = complaintService.getOne(wrapper);
            //获取订单金额 单位 元
            BigDecimal price = null;
            //如果没有投诉，直接获取订单金额
            if (complaint != null){
                price = complaint.getPrice();
            }else {
                price = order.getTotalPrice();
            }
            price = BigDecimal.valueOf(price.intValue() * 100);
            //如果订单有短信提醒，扣取短信费用
            if (order.getSmsNotify() == null){
                price.subtract(order.getSmsPrice());
            }

            //创建微信退款请求Authorization
            Config config = new RSAAutoCertificateConfig.Builder()
                            .merchantId(merchantId)
                            .privateKey(pem_key)
                            .merchantSerialNumber(merchantSerialNumber)
                            .apiV3Key(apiKey)
                            .build();

            //创建jsapiService
            JsapiService jsapiBuild = new JsapiService.Builder().config(config).build();
            QueryOrderByOutTradeNoRequest queryOrderByOutTradeNoRequest = new QueryOrderByOutTradeNoRequest();
            queryOrderByOutTradeNoRequest.setOutTradeNo(tradeNo);
            queryOrderByOutTradeNoRequest.setMchid(merchantId);
            Transaction transaction = jsapiBuild.queryOrderByOutTradeNo(queryOrderByOutTradeNoRequest);

            //获取订单客户实际付款金额，单位 分
            Integer total = transaction.getAmount().getTotal();

            //创建退款请求
            com.wechat.pay.java.service.refund.RefundService refundBuild = new com.wechat.pay.java.service.refund.RefundService.Builder().config(config).build();
            CreateRequest createRequest = new CreateRequest();

            //设置退款参数
            createRequest.setOutTradeNo(tradeNo);
            createRequest.setOutRefundNo("Refund_" + tradeNo);
            AmountReq amount = new AmountReq();
            amount.setRefund(Double.doubleToLongBits(price.intValue() * 0.95));
            amount.setCurrency("CNY");
            amount.setTotal(Long.valueOf(total));
            createRequest.setAmount(amount);
            //执行退款
            Refund refund = refundBuild.create(createRequest);
            //获取退款单号
            String refundId = refund.getRefundId();
            //判断是否退款成功
            if (refundId == null){
                log.info("微信退款失败: {},进行递归确保退款成功", tradeNo);
                wxRefund(order,ca,++l);
                return;
            }
            //更新退款单号
            RefundVerify refundVerify = refundVerifyService.getById(tradeNo);
            refundVerify.setRefundOrderId(refundId);
            //判断退款状态
            if (refund.getStatus().equals(Status.SUCCESS)) {
                refundVerify.setStatus(3);
            }
            //更新退款单
            refundVerifyService.saveOrUpdate(refundVerify);
        }catch (Exception e){
            throw new RuntimeException(e.getMessage(),e);
        }

    }

    /**
     * 获取支付宝退款状态
     * @param tradeNo
     * @param appId
     * @param privateKey
     * @param alipayPublicKey
     * @return
     * @throws AlipayApiException
     */
    private int getAliRefundStatus(String tradeNo, String appId, String privateKey, String alipayPublicKey) throws AlipayApiException {
        AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do",appId,privateKey,"json","utf-8",alipayPublicKey,"RSA2");
        AlipayTradeFastpayRefundQueryRequest request = new AlipayTradeFastpayRefundQueryRequest();
        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no", tradeNo);
        bizContent.put("out_request_no","Refund" + tradeNo);

        request.setBizContent(bizContent.toString());
        AlipayTradeFastpayRefundQueryResponse response = alipayClient.execute(request);
        if(response.isSuccess()){
            if (!response.getRefundStatus().equals("REFUND_SUCCESS")) {
                return 2;
            }else {
                return 3;
            }
        } else {
            throw new RuntimeException("退款状态查询"+ tradeNo +"调用失败--" + response.getSubMsg());
        }

    }

    /**
     * 获取支付宝订单金额
     * @param tradeNo
     * @param appId
     * @param privateKey
     * @param alipayPublicKey
     * @return
     * @throws AlipayApiException
     */
    private Double getAliOrderMoney(String tradeNo,String appId,String privateKey,String alipayPublicKey) throws AlipayApiException {
        AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do",appId,privateKey,"json","utf-8",alipayPublicKey,"RSA2");
        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        request.setBizContent("{" +
                "  \"out_trade_no\":\"" + tradeNo + "\"," +
                "  \"query_options\":[" +
                "    \"trade_settle_info\",\"fund_bill_list\",\"voucher_detail_list\",\"discount_goods_detail\",\"mdiscount_amount\",\"medical_insurance_info\"" +
                "  ]" +
                "}");
        AlipayTradeQueryResponse response = alipayClient.execute(request);
        if(response.isSuccess()){
            String receiptAmount = response.getReceiptAmount();
            return new BigDecimal(Double.valueOf(receiptAmount) * 0.95).setScale(2, BigDecimal.ROUND_DOWN).doubleValue();
        } else {
            throw new RuntimeException("订单信息查询"+ tradeNo +"调用失败--" + response.getSubMsg());
        }
    }


}
